{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# SVD推荐算法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "unexpected EOF while parsing (<ipython-input-1-18b78add98e4>, line 6)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;36m  File \u001b[1;32m\"<ipython-input-1-18b78add98e4>\"\u001b[1;36m, line \u001b[1;32m6\u001b[0m\n\u001b[1;33m    def __init__(self,mark,K = 20):\u001b[0m\n\u001b[1;37m                                   ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m unexpected EOF while parsing\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import random\n",
    "\n",
    "\n",
    "class SVD:  \n",
    "\n",
    "    def __init__(self,mat,K=20):  \n",
    "\n",
    "        self.mat=np.array(mat)  \n",
    "\n",
    "        self.K=K  \n",
    "\n",
    "        self.bi={}  \n",
    "\n",
    "        self.bu={}  \n",
    "\n",
    "        self.qi={}  \n",
    "\n",
    "        self.pu={}  \n",
    "\n",
    "        self.avg=np.mean(self.mat[:,2])  \n",
    "\n",
    "        for i in range(self.mat.shape[0]):  \n",
    "\n",
    "            uid=self.mat[i,0]  \n",
    "\n",
    "            iid=self.mat[i,1]  \n",
    "\n",
    "            self.bi.setdefault(iid,0)  \n",
    "\n",
    "            self.bu.setdefault(uid,0)  \n",
    "\n",
    "            self.qi.setdefault(iid,np.random.random((self.K,1))/10*np.sqrt(self.K))  \n",
    "\n",
    "            self.pu.setdefault(uid,np.random.random((self.K,1))/10*np.sqrt(self.K))  \n",
    "\n",
    "    def predict(self,uid,iid):  #预测评分的函数  \n",
    "\n",
    "        #setdefault的作用是当该用户或者物品未出现过时，新建它的bi,bu,qi,pu，并设置初始值为0  \n",
    "\n",
    "        self.bi.setdefault(iid,0)  \n",
    "\n",
    "        self.bu.setdefault(uid,0)  \n",
    "\n",
    "        self.qi.setdefault(iid,np.zeros((self.K,1)))  \n",
    "\n",
    "        self.pu.setdefault(uid,np.zeros((self.K,1)))  \n",
    "\n",
    "        rating=self.avg+self.bi[iid]+self.bu[uid]+np.sum(self.qi[iid]*self.pu[uid]) #预测评分公式  \n",
    "\n",
    "        #由于评分范围在1到5，所以当分数大于5或小于1时，返回5,1.  \n",
    "\n",
    "        if rating>5:  \n",
    "\n",
    "            rating=5  \n",
    "\n",
    "        if rating<1:  \n",
    "\n",
    "            rating=1  \n",
    "\n",
    "        return rating  \n",
    "\n",
    "      \n",
    "\n",
    "    def train(self,steps=30,gamma=0.04,Lambda=0.15):    #训练函数，step为迭代次数。  \n",
    "\n",
    "        print('train data size',self.mat.shape)  \n",
    "\n",
    "        for step in range(steps):  \n",
    "\n",
    "            print('step',step+1,'is running')  \n",
    "\n",
    "            KK=np.random.permutation(self.mat.shape[0]) #随机梯度下降算法，kk为对矩阵进行随机洗牌  \n",
    "\n",
    "            rmse=0.0  \n",
    "\n",
    "            for i in range(self.mat.shape[0]):  \n",
    "\n",
    "                j=KK[i]  \n",
    "\n",
    "                uid=self.mat[j,0]  \n",
    "\n",
    "                iid=self.mat[j,1]  \n",
    "\n",
    "                rating=self.mat[j,2]  \n",
    "\n",
    "                eui=rating-self.predict(uid, iid)  \n",
    "\n",
    "                rmse+=eui**2  \n",
    "\n",
    "                self.bu[uid]+=gamma*(eui-Lambda*self.bu[uid])    \n",
    "\n",
    "                self.bi[iid]+=gamma*(eui-Lambda*self.bi[iid])  \n",
    "\n",
    "                tmp=self.qi[iid]  \n",
    "\n",
    "                self.qi[iid]+=gamma*(eui*self.pu[uid]-Lambda*self.qi[iid])  \n",
    "\n",
    "                self.pu[uid]+=gamma*(eui*tmp-Lambda*self.pu[uid])  \n",
    "\n",
    "            gamma=0.93*gamma  \n",
    "\n",
    "            print('rmse is',np.sqrt(rmse/self.mat.shape[0]))  \n",
    "\n",
    "      \n",
    "\n",
    "    def test(self,test_data):  #gamma以0.93的学习率递减  \n",
    "\n",
    "          \n",
    "\n",
    "        test_data=np.array(test_data)  \n",
    "\n",
    "        print('test data size',test_data.shape)  \n",
    "\n",
    "        rmse=0.0  \n",
    "\n",
    "        for i in range(test_data.shape[0]):  \n",
    "\n",
    "            uid=test_data[i,0]  \n",
    "\n",
    "            iid=test_data[i,1]  \n",
    "\n",
    "            rating=test_data[i,2]  \n",
    "\n",
    "            eui=rating-self.predict(uid, iid)  \n",
    "\n",
    "            rmse+=eui**2  \n",
    "\n",
    "        print('rmse of test data is',np.sqrt(rmse/test_data.shape[0]))  \n",
    "\n",
    "              \n",
    "\n",
    "              \n",
    "\n",
    "def getData():   #获取训练集和测试集的函\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import re  \n",
    "\n",
    "    f=open('C:/Users/xuwei/Desktop/data.txt','r')  \n",
    "\n",
    "    lines=f.readlines()  \n",
    "\n",
    "    f.close()  \n",
    "\n",
    "    data=[]  \n",
    "\n",
    "    for line in lines:  \n",
    "\n",
    "        list=re.split('\\t|\\n',line)  \n",
    "\n",
    "        if int(list[2]) !=0:    #提出评分0的数据，这部分是用户评论了但是没有评分的  \n",
    "\n",
    "            data.append([int(i) for i in list[:3]])  \n",
    "\n",
    "    random.shuffle(data)  \n",
    "\n",
    "    train_data=data[:int(len(data)*7/10)]  \n",
    "\n",
    "    test_data=data[int(len(data)*7/10):]  \n",
    "\n",
    "    print('load data finished')  \n",
    "\n",
    "    print('total data ',len(data))  \n",
    "\n",
    "    return train_data,test_data  \n",
    "\n",
    "      \n",
    "\n",
    "     \n",
    "\n",
    "  \n",
    "\n",
    "train_data,test_data=getData()  \n",
    "\n",
    "a=SVD(train_data,30)    \n",
    "\n",
    "a.train()  \n",
    "\n",
    "a.test(test_data)  \n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
